package org.ehe.erp.service.finance.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.ehe.business.supplier.domain.SupplierInfo;
import org.ehe.business.supplier.service.SupplierInfoService;
import org.ehe.common.core.exception.ServiceException;
import org.ehe.common.core.utils.MapstructUtils;
import org.ehe.common.mybatis.core.page.PageQuery;
import org.ehe.common.mybatis.core.page.TableDataInfo;
import org.ehe.erp.domain.finance.ErpFinancePayment;
import org.ehe.erp.domain.finance.bo.ErpFinancePaymentBo;
import org.ehe.erp.domain.finance.vo.ErpAccountVo;
import org.ehe.erp.domain.finance.vo.ErpFinancePaymentVo;
import org.ehe.erp.domain.batch.vo.ErpBatchVo;
import org.ehe.erp.domain.purchase.ErpPurchaseOrder;
import org.ehe.erp.generate.service.GenerateNoService;
import org.ehe.erp.mapper.finance.ErpFinancePaymentMapper;
import org.ehe.erp.service.batch.ErpBatchInfoService;
import org.ehe.erp.service.finance.ErpFinancePaymentService;
import org.ehe.erp.service.purchase.IErpPurchaseOrderService;
import org.ehe.finance.payable.domain.ErpFinancePayable;
import org.ehe.finance.payable.domain.bo.PayableBo;
import org.ehe.finance.payable.service.PayableService;
import org.ehe.system.domain.vo.SysUserVo;
import org.ehe.system.service.ISysUserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * ERP付款单ServiceImpl
 * @author :zhangnn
 * @className :ErpFinancePaymentServiceImpl
 * @description: TODO
 * @date 2025-08-18 14:28:00
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ErpFinancePaymentServiceImpl extends ServiceImpl<ErpFinancePaymentMapper, ErpFinancePayment> implements ErpFinancePaymentService {

    private final SupplierInfoService supplierInfoService;
    private final ErpBatchInfoService batchInfoService;
    private final ErpAccountServiceImpl accountService;
    private final ISysUserService userService;
    private final IErpPurchaseOrderService purchaseOrderService;
    private final PayableService payableService;
    //生成编码
    private final GenerateNoService generateNoService;
    /**
     * 付款单状态枚举
     */
    public static class PaymentStatus {
        public static final Integer DRAFT = 0;      // 草稿
        public static final Integer AUDITED = 1;    // 已审核

        public static final Integer UNAUDITED = 2;    // 待审核（未支付）
        public static final Integer PAID = 3;       // 已付款

        public static final Integer REJECTED = 4;       // 审核拒绝（未支付）

        public static final Integer CANCELLED = 9;  // 已取消
    }

    @Override
    public ErpFinancePaymentVo queryById(Long id) {
        return baseMapper.selectVoById(id);
    }

    @Override
    public List<ErpFinancePaymentVo> queryList(ErpFinancePaymentBo bo) {
        LambdaQueryWrapper<ErpFinancePayment> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    @Override
    public TableDataInfo<ErpFinancePaymentVo> queryPageList(ErpFinancePaymentBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<ErpFinancePayment> lqw = buildQueryWrapper(bo);
        Page<ErpFinancePaymentVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        result.getRecords().forEach(p->{
            SupplierInfo supplierInfo = supplierInfoService.getById(p.getSupplierId());
            p.setSupplierName(supplierInfo.getSupplierName());
            //采购订单信息
            ErpPurchaseOrder order = purchaseOrderService.queryByOrderNo(p.getPurchaseOrderNo());
            if(Objects.nonNull(order)){
                p.setPurchaseOrderName(order.getName());
            }

            ErpAccountVo erpAccountVo = accountService.queryById(p.getAccountId());
            if (Objects.nonNull(erpAccountVo)) {
                p.setAccountName(erpAccountVo.getName());
            }
            SysUserVo sysUserVo = userService.selectUserById(p.getFinanceUserId());
            p.setFinanceUserName(sysUserVo.getNickName());

            ErpBatchVo erpBatchVo = batchInfoService.queryById(p.getBatchId());
            if(Objects.nonNull(erpBatchVo)){
                p.setBatchName(erpBatchVo.getTypeName());
            }
        });


        return TableDataInfo.build(result);
    }

    /**
     * 构建查询条件
     */
    private LambdaQueryWrapper<ErpFinancePayment> buildQueryWrapper(ErpFinancePaymentBo bo) {
        Map<String, Object> params = BeanUtil.beanToMap(bo, false, true);
        LambdaQueryWrapper<ErpFinancePayment> lqw = Wrappers.lambdaQuery();

        lqw.like(StringUtils.isNotBlank(bo.getNo()), ErpFinancePayment::getNo, bo.getNo());
        lqw.like(StringUtils.isNotBlank(bo.getPurchaseOrderNo()), ErpFinancePayment::getPurchaseOrderNo, bo.getPurchaseOrderNo());
        lqw.eq(ObjectUtil.isNotNull(bo.getStatus()), ErpFinancePayment::getStatus, bo.getStatus());
        lqw.eq(ObjectUtil.isNotNull(bo.getFinanceUserId()), ErpFinancePayment::getFinanceUserId, bo.getFinanceUserId());
        lqw.eq(ObjectUtil.isNotNull(bo.getSupplierId()), ErpFinancePayment::getSupplierId, bo.getSupplierId());
        lqw.eq(ObjectUtil.isNotNull(bo.getAccountId()), ErpFinancePayment::getAccountId, bo.getAccountId());
        lqw.like(ObjectUtil.isNotNull(bo.getPaymentTime()), ErpFinancePayment::getPaymentTime, bo.getPaymentTime());

        lqw.orderByDesc(ErpFinancePayment::getCreateTime);
        return lqw;
    }

    /**
     * 新增ERP付款单
     * @param bos ERP付款单业务对象
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean insertByBo(List<ErpFinancePaymentBo> bos) {
        if (CollectionUtils.isEmpty(bos)) {
            log.warn("批量插入付款单失败：传入的付款单列表为空");
            return false;
        }

        // 1. 校验批次信息（必须包含批次序号，且属于同一采购订单）
        String purchaseOrderNo = bos.get(0).getPurchaseOrderNo();
        if (bos.stream().anyMatch(bo -> !Objects.equals(bo.getPurchaseOrderNo(), purchaseOrderNo))) {
            throw new RuntimeException("批量付款单必须属于同一采购订单");
        }
        // 校验所有批次都包含批次序号（batchNo）
        if (bos.stream().anyMatch(bo -> bo.getNo() == null)) {
            throw new RuntimeException("付款单批次序号（batchNo）不能为空");
        }

        // 2. 查询采购订单和供应商信息
        ErpPurchaseOrder order = purchaseOrderService.queryByOrderNo(purchaseOrderNo);
        if (Objects.isNull(order)) {
            throw new RuntimeException("采购订单不存在：" + purchaseOrderNo);
        }
        SupplierInfo supplierInfo = supplierInfoService.getById(order.getSupplierId());
        if (Objects.isNull(supplierInfo)) {
            throw new RuntimeException("供应商不存在：" + order.getSupplierId());
        }

        // 3. 批量插入付款单（按批次序号排序，确保顺序正确）
        List<ErpFinancePaymentBo> sortedBos = bos.stream()
            .sorted(Comparator.comparingLong(ErpFinancePaymentBo::getBatchId)) // 按批次序号升序
            .collect(Collectors.toList());

        List<ErpFinancePayment> payments = new ArrayList<>();
        for (ErpFinancePaymentBo bo : sortedBos) {
            ErpFinancePayment add = MapstructUtils.convert(bo, ErpFinancePayment.class);

            // 补充基础信息
            add.setSupplierId(Objects.nonNull(add.getSupplierId()) ? add.getSupplierId() : supplierInfo.getId());
            add.setNo(generatePaymentNo("PAY"));
            add.setPurchaseOrderNo(purchaseOrderNo);
            add.setStatus(Objects.nonNull(add.getStatus()) ? add.getStatus() : PaymentStatus.DRAFT); // 默认为草稿（未支付）

            // 计算实付金额
//            if (Objects.isNull(add.getPaymentPrice())) {
//                BigDecimal totalPrice = Objects.nonNull(add.getTotalPrice()) ? add.getTotalPrice() : BigDecimal.ZERO;
//                BigDecimal discountPrice = Objects.nonNull(add.getDiscountPrice()) ? add.getDiscountPrice() : BigDecimal.ZERO;
//                add.setPaymentPrice(totalPrice.subtract(discountPrice));
//            }
            validEntityBeforeSave(add);
            payments.add(add);
            bo.setId(add.getId());
        }

        // 批量插入付款单
        boolean batchInsertFlag = baseMapper.insertBatch(payments);
        if (!batchInsertFlag) {
            throw new RuntimeException("批量插入付款单失败");
        }

        // 4. 计算应付账款的初始到期日（取第一个未支付批次的付款时间）
        LocalDateTime initialDueDate = calculateInitialDueDate(sortedBos);

        // 5. 创建应付账款（关联采购订单和到期日）
        PayableBo payableBo = new PayableBo();
        payableBo.setBillDate(LocalDate.now());
        payableBo.setBillNo(generatePaymentNo("BN."));
        payableBo.setSourceBillNo(purchaseOrderNo);
        payableBo.setSupplierId(supplierInfo.getId());
        payableBo.setTotalAmount(order.getTotalPrice()); // 应付总金额=采购订单总金额
        payableBo.setBillType(1); // 采购发票类型
        payableBo.setDueDate(initialDueDate.toLocalDate()); // 设置初始到期日（转为LocalDate）
        payableBo.setSettlementStatus(1); // 初始状态：未结算

        boolean createPayableFlag = payableService.createPayable(payableBo);
        if (!createPayableFlag) {
            throw new RuntimeException("创建应付账款失败，采购订单号：" + purchaseOrderNo);
        }

        // 6. 关联付款单与应付账款
        Long payableId = getPayableIdByBillNo(payableBo.getBillNo());
        for (ErpFinancePayment payment : payments) {
            payment.setPayableId(payableId);
            baseMapper.updateById(payment);
        }

        return true;
    }

    /**
     * 计算初始到期日：取第一个未支付批次的payment_time
     */
    private LocalDateTime calculateInitialDueDate(List<ErpFinancePaymentBo> sortedBos) {
        // 按批次序号升序遍历，找到第一个未支付的批次
        for (ErpFinancePaymentBo bo : sortedBos) {
            // 未支付状态：草稿（1）、未审核（可根据实际枚举值调整）
            if (bo.getStatus() == null || bo.getStatus() == PaymentStatus.DRAFT || bo.getStatus() == PaymentStatus.UNAUDITED) {
                if (Objects.isNull(bo.getPaymentTime())) {
                    throw new RuntimeException("未支付批次（" + bo.getNo() + "）的付款时间（paymentTime）不能为空");
                }
                return bo.getPaymentTime(); // 返回该批次的付款时间作为到期日
            }
        }
        // 若所有批次均已支付，返回最后一个批次的付款时间
        return sortedBos.get(sortedBos.size() - 1).getPaymentTime();
    }

    /**
     * 根据应付单据号查询ID
     */
    private Long getPayableIdByBillNo(String billNo) {
        ErpFinancePayable payable = payableService.getOne(new LambdaQueryWrapper<ErpFinancePayable>().eq(ErpFinancePayable::getBillNo,billNo));
        return Objects.nonNull(payable) ? payable.getId() : null;
    }


    /**
     * 修改
     * @param bos ERP付款单业务对象
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateByBo(List<ErpFinancePaymentBo> bos) {
        Long payableId = null;
        for (ErpFinancePaymentBo bo : bos) {
            // 1. 基础校验
            if (ObjectUtil.isNull(bo.getId())) {
                throw new ServiceException("付款单ID不能为空");
            }
            ErpFinancePayment oldPayment = baseMapper.selectById(bo.getId());
            if (ObjectUtil.isNull(oldPayment)) {
                throw new ServiceException("付款单不存在，ID：" + bo.getId());
            }
            payableId = oldPayment.getPayableId();
            if (ObjectUtil.isNull(payableId)) {
                throw new ServiceException("付款单未关联应付账款，无法同步更新");
            }

            // 2. 转换并更新付款单数据（单位：元，无需转换）
            ErpFinancePayment update = MapstructUtils.convert(bo, ErpFinancePayment.class);
            // 保留原有关联关系
            update.setPayableId(payableId);
            update.setPurchaseOrderNo(oldPayment.getPurchaseOrderNo());

            // 3. 重新计算实付金额（直接基于元单位计算）
//            if (ObjectUtil.isNotNull(update.getTotalPrice())) {
//                BigDecimal totalPrice = update.getTotalPrice();
//                BigDecimal discountPrice = ObjectUtil.defaultIfNull(update.getDiscountPrice(), BigDecimal.ZERO);
//                // 直接计算（元 - 元 = 元）
//                update.setPaymentPrice(totalPrice.subtract(discountPrice));
//            }

            // 4. 执行付款单更新
            validEntityBeforeSave(update);
            int updateCount = baseMapper.updateById(update);
            if (updateCount <= 0) {
                log.info("付款单未发生变更，ID：{}", bo.getId());
                return false;
            }
        }
        // 5. 同步更新应付账款
        syncPayableData(payableId);
        return true;
    }

    /**
     * 同步更新应付账款数据（单位：元）
     * @param payableId 应付账款ID
     */
    private void syncPayableData(Long payableId) {
        // 5.1 查询该应付账款关联的所有有效付款单
        List<ErpFinancePayment> paymentList = this.baseMapper.selectByPayableId(payableId);
        if (CollectionUtil.isEmpty(paymentList)) {
            log.warn("应付账款无关联付款单，ID：{}", payableId);
            return;
        }

        // 5.2 计算应付账款的最新已付金额
        BigDecimal totalPaid = paymentList.stream()
            .filter(payment -> PaymentStatus.AUDITED.equals(payment.getStatus())) // 仅统计已支付
            .map(ErpFinancePayment::getPaymentPrice)
            .filter(Objects::nonNull)
            .reduce(BigDecimal.ZERO, BigDecimal::add);

        // 5.3 计算最新到期日
        LocalDate newDueDate = calculateDueDate(paymentList);

        // 5.4 构造应付账款更新参数
        ErpFinancePayable payable = payableService.getById(payableId);
        if (ObjectUtil.isNull(payable)) {
            throw new ServiceException("应付账款不存在，ID：" + payableId);
        }
        PayableBo updateBo = new PayableBo();
        BeanUtil.copyProperties(payable, updateBo);
        updateBo.setPaidAmount(totalPaid); // 更新已付金额
        updateBo.setDueDate(newDueDate); // 更新到期日

        // 5.5 调用应付账款修改方法
        payableService.updatePayable(updateBo);
        log.info("应付账款已同步更新，ID：{}，最新已付金额：{}元，到期日：{}", payableId, totalPaid, newDueDate);
    }

    /**
    * 计算应付账款最新到期日
    * @param paymentList 付款单列表
    * @return 到期日（最早未支付批次的付款时间）
    */
    private LocalDate calculateDueDate(List<ErpFinancePayment> paymentList) {
        // 按批次号升序排序
        List<ErpFinancePayment> sortedPayments = paymentList.stream()
            .sorted(Comparator.comparingLong(ErpFinancePayment::getBatchId))
            .toList();

        // 查找第一个未支付的批次
        for (ErpFinancePayment payment : sortedPayments) {
            if (!PaymentStatus.AUDITED.equals(payment.getStatus())) { // 未支付状态
                return payment.getPaymentTime().toLocalDate();
            }
        }

        // 所有批次已支付，取最后一个批次的付款时间
        return sortedPayments.get(sortedPayments.size() - 1).getPaymentTime().toLocalDate();
    }


    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(ErpFinancePayment entity) {
        // 校验付款单号唯一性
        if (StringUtils.isNotBlank(entity.getNo()) && null == entity.getId()) {
            long count = baseMapper.selectCount(Wrappers.<ErpFinancePayment>lambdaQuery()
                .eq(ErpFinancePayment::getNo, entity.getNo())
                .ne(ObjectUtil.isNotNull(entity.getId()), ErpFinancePayment::getId, entity.getId()));
            if (count > 0) {
                throw new RuntimeException("付款单号已存在：" + entity.getNo());
            }
        }

        // 校验金额合理性
        if (ObjectUtil.isNotNull(entity.getTotalPrice()) && entity.getTotalPrice().compareTo(BigDecimal.ZERO) <= 0) {
            throw new RuntimeException("合计价格必须大于0");
        }

        if (ObjectUtil.isNotNull(entity.getDiscountPrice()) && entity.getDiscountPrice().compareTo(BigDecimal.ZERO) < 0) {
            throw new RuntimeException("优惠金额不能为负数");
        }

        if (ObjectUtil.isNotNull(entity.getPaymentPrice()) && entity.getPaymentPrice().compareTo(BigDecimal.ZERO) <= 0) {
            throw new RuntimeException("实付金额必须大于0");
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleteWithValidByIds(Collection<Long> ids) {
        // 检查是否可以删除
        for (Long id : ids) {
            if (!checkCanDelete(id)) {
                ErpFinancePayment payment = baseMapper.selectById(id);
                throw new RuntimeException("付款单 [" + payment.getNo() + "] 已审核，无法删除");
            }
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    /**
     * 审核付款单
     * @param id 付款单ID
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean auditPayment(Long id) {
        if (!checkCanAudit(id)) {
            throw new RuntimeException("付款单状态不正确，无法审核");
        }

        LambdaUpdateWrapper<ErpFinancePayment> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.eq(ErpFinancePayment::getId, id);
        updateWrapper.set(ErpFinancePayment::getStatus, PaymentStatus.AUDITED);

        return baseMapper.update(null, updateWrapper) > 0;
    }

    /**
     * 撤销审核付款单
     * @param id 付款单ID
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean unauditPayment(Long id) {
        ErpFinancePayment payment = baseMapper.selectById(id);
        if (ObjectUtil.isNull(payment)) {
            throw new RuntimeException("付款单不存在");
        }

        if (!PaymentStatus.AUDITED.equals(payment.getStatus())) {
            throw new RuntimeException("只有已审核状态的付款单才能撤销审核");
        }

        LambdaUpdateWrapper<ErpFinancePayment> updateWrapper = Wrappers.lambdaUpdate();
        updateWrapper.eq(ErpFinancePayment::getId, id);
        updateWrapper.set(ErpFinancePayment::getStatus, PaymentStatus.DRAFT);

        return baseMapper.update(null, updateWrapper) > 0;
    }

    @Override
    public ErpFinancePaymentVo getPaymentStatistics() {
        // 统计今日付款金额
        LocalDateTime startOfDay = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0).withNano(0);
        LocalDateTime endOfDay = startOfDay.plusDays(1);

        LambdaQueryWrapper<ErpFinancePayment> todayWrapper = Wrappers.lambdaQuery();
        todayWrapper.ge(ErpFinancePayment::getPaymentTime, startOfDay);
        todayWrapper.lt(ErpFinancePayment::getPaymentTime, endOfDay);
        todayWrapper.ne(ErpFinancePayment::getStatus, PaymentStatus.CANCELLED);

        List<ErpFinancePayment> todayPayments = baseMapper.selectList(todayWrapper);
        BigDecimal todayTotal = todayPayments.stream()
            .map(ErpFinancePayment::getPaymentPrice)
            .reduce(BigDecimal.ZERO, BigDecimal::add);

        // 统计本月付款金额
        LocalDateTime startOfMonth = LocalDateTime.now().withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0).withNano(0);

        LambdaQueryWrapper<ErpFinancePayment> monthWrapper = Wrappers.lambdaQuery();
        monthWrapper.ge(ErpFinancePayment::getPaymentTime, startOfMonth);
        monthWrapper.ne(ErpFinancePayment::getStatus, PaymentStatus.CANCELLED);

        List<ErpFinancePayment> monthPayments = baseMapper.selectList(monthWrapper);
        BigDecimal monthTotal = monthPayments.stream()
            .map(ErpFinancePayment::getPaymentPrice)
            .reduce(BigDecimal.ZERO, BigDecimal::add);

        ErpFinancePaymentVo statistics = new ErpFinancePaymentVo();
        statistics.setTotalPrice(todayTotal); // 今日付款金额
        statistics.setPaymentPrice(monthTotal); // 本月付款金额

        return statistics;
    }

    @Override
    public List<ErpFinancePaymentVo> getPaymentsBySupplier(Long supplierId) {
        LambdaQueryWrapper<ErpFinancePayment> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(ErpFinancePayment::getSupplierId, supplierId);
        wrapper.orderByDesc(ErpFinancePayment::getPaymentTime);
        return baseMapper.selectVoList(wrapper);
    }

    /**
     * 生成支付编号
     * @return
     */
    @Override
    public String generatePaymentNo(String prefix) {
        String s = generateNoService.generatePaymentNo(prefix);
        System.out.println("生成的编码："+s);
        return s;
    }

    @Override
    public Boolean checkCanDelete(Long id) {
        ErpFinancePayment payment = baseMapper.selectById(id);
        if (ObjectUtil.isNull(payment)) {
            return false;
        }
        // 只有草稿状态的付款单才能删除
        return PaymentStatus.DRAFT.equals(payment.getStatus());
    }

    @Override
    public Boolean checkCanAudit(Long id) {
        ErpFinancePayment payment = baseMapper.selectById(id);
        if (ObjectUtil.isNull(payment)) {
            return false;
        }
        // 只有草稿状态的付款单才能审核
        return PaymentStatus.DRAFT.equals(payment.getStatus());
    }

    /**
     * 根据付款单号查询付款单名称
     * @param no
     * @return
     */
    @Override
    public ErpFinancePayment queryByNo(String no) {
        return this.baseMapper.selectOne(new LambdaQueryWrapper<ErpFinancePayment>().eq(ErpFinancePayment::getNo,no));
    }
}
